#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>

#include "input.h"
#include "simpleList.h"
#include "listcmp.h"

#define TIMER
#define DEBUG

#define MASTER 0
#define TAG 0

int main(int argc, char *argv[])
{

    // MPI Control
    int my_rank;
    int nProcess;
    MPI_Status status;

    int SLAVE;
    int PC;
    
    unsigned long R; // random word size
    unsigned long V; // Vector size
    unsigned long S; // WL size

	// cria as estruturas
    struct Vector *Partial;
    struct wordList *WL;
    struct Percentage Progress;

	// calcula o tempo gasto
    #ifdef TIMER
        struct timeval t_zero, t_final;
        double delta_t;
        gettimeofday(&t_zero, NULL);
    #endif

	// inicializa mpi
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &nProcess);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

	// marca prograsso com 0
    Progress.total  = WL-> size;
    Progress.part = 0;
    Progress.percent = 0;
    
	// verifica se passou o nome do arquivo como argumento
    if(argc != 2)
	{
        printf("Uso incorreto: %s <arquivo.txt>", argv[0]);
        exit(-1);
    }

    // leitura do texto
    WL = readInput(argv[1]);

begin:

	// no mestre
    if(my_rank == MASTER)
	{
       
        while(1)
		{        

            printf("\r Progress: %.0d%%", Progress.percent);
 
            for(SLAVE = 1; SLAVE < nProcess; SLAVE++)
			{

                // The number of places 
                MPI_Recv(&V, 1, MPI_UNSIGNED_LONG, SLAVE, SLAVE, MPI_COMM_WORLD, &status);
                
				// cria um vetor parcial
                Partial = initVector(V);
 
                // The places
                MPI_Recv(Partial->v, V, MPI_UNSIGNED_LONG, SLAVE, SLAVE, MPI_COMM_WORLD, &status);
 
                // The size of the random word    
                MPI_Recv(&R, 1, MPI_UNSIGNED_LONG, SLAVE, SLAVE, MPI_COMM_WORLD, &status);

                if(R > 5) { exit(-1); }
               
			    // junta o vetor na lista
                glueChunks(WL, Partial, R);
				
				// libera o vetor
                freeVector(Partial);
            }

            S = WL->size;

            // Bcast the new size;
            MPI_Bcast(&S, 1, MPI_UNSIGNED_LONG, MASTER, MPI_COMM_WORLD);                
            

            // Bcast the new list;
            MPI_Bcast(WL->list, S, MPI_CHAR, MASTER, MPI_COMM_WORLD);

            memset(WL->mark, 0, S);

			// atualiza progresso
            PC = updateStatus(WL, &Progress);
			
			// para quando cegar a 100%
            if(PC == 100) { break; }              
            
        }
    }
    // my_rank==SLAVE 
    else
	{
        char random_string[6];

        while(1)
		{
            while(WL->size > 2)
			{
				// gera a palavra
                str_gen(random_string);
				// verifica se esta na lista de palavras
                Partial = lstcmp(WL, random_string);
                if(Partial != NULL){ break; }
            }
    
            V = Partial->length;

            // The number of places 
            MPI_Send(&V, 1, MPI_UNSIGNED_LONG, MASTER, my_rank, MPI_COMM_WORLD);
    
            // The places
            MPI_Send(Partial->v, V, MPI_UNSIGNED_LONG, MASTER, my_rank, MPI_COMM_WORLD);
 
            R = strlen(random_string);
 
            // SIZE OF THE RANDOM STRiNG
            MPI_Send(&R, 1, MPI_UNSIGNED_LONG, MASTER, my_rank, MPI_COMM_WORLD);
   
            // RECIVE DATA
            MPI_Bcast(&S, 1, MPI_UNSIGNED_LONG, MASTER, MPI_COMM_WORLD);                
        
            WL->size = S;
            memset(WL->list, 0, S);

            MPI_Bcast(WL->list, S, MPI_CHAR, MASTER, MPI_COMM_WORLD);                
            
            memset(WL->mark, 0, S);
           
            freeVector(Partial);
        }   
    }

end:
	
	// mostra o tempo gasto
    #ifdef TIMER
        gettimeofday(&t_final, NULL);
        delta_t = t_final.tv_sec - t_zero.tv_sec +
        ( t_final.tv_usec - t_zero.tv_usec ) / 1e6;

        printf("Tempo = %.3lf segundos\n", delta_t);
    #endif

    MPI_Finalize();
    MPI_Abort(MPI_COMM_WORLD, 666);    
    
    return 0;
}
